Skip to content

Manage local Goose schedules from Berd - #245

Open
NickTitle wants to merge 1 commit into
mainfrom
nesposito/live-schedule-control-block
Open

Manage local Goose schedules from Berd#245
NickTitle wants to merge 1 commit into
mainfrom
nesposito/live-schedule-control-block

Conversation

@NickTitle

Copy link
Copy Markdown
Contributor

Summary

  • show local Goose schedules in Berd's Automations view
  • pause, resume, stop active runs, and remove schedules through the live embedded scheduler
  • expose the same controls as berdctl schedule commands
  • require explicit confirmation for CLI schedule removal

Why

Berd starts a long-lived goosed --enable-scheduler process, but local Goose schedules were invisible in Berd and could only be managed with a separate goose schedule CLI process. That creates split scheduler state: an out-of-process removal can update schedule.json while the embedded scheduler still holds the job in memory, allowing a later status write to restore the deleted job.

This change routes user and agent controls through Berd's live ACP client, so they operate on the scheduler instance that actually owns and runs the jobs.

User-visible behavior

The Automations overview now has a Local schedules section. Each job shows its cron and running/paused state, with actions to:

  • pause or resume future runs
  • stop the active run without removing the schedule
  • remove the schedule registration after confirmation (the saved recipe file is kept)

Agents get matching commands:

berdctl schedule list
berdctl schedule pause --schedule-id <id>
berdctl schedule unpause --schedule-id <id>
berdctl schedule kill --schedule-id <id>
berdctl schedule remove --schedule-id <id> --confirm

Validation

  • just check
  • just test — 6,000+ frontend tests pass
  • cargo test -p berdctl — 52 passed, 1 ignored
  • pre-push fmt-check, check, tauri-check, and clippy all pass

Architectural laws

Read LAWS/README.md, LAWS/AGENTS.md, and LAWS/CHAT.md. This change does not alter agent invocation or composer queue behavior. The UI and CLI operate on the same live schedule state.

@NickTitle
NickTitle requested a review from a team August 28, 2026 14:51
@NickTitle
NickTitle force-pushed the nesposito/live-schedule-control-block branch 3 times, most recently from 4851df0 to 4731727 Compare August 28, 2026 15:53
Co-authored-by: Goose <opensource@block.xyz>
@NickTitle
NickTitle force-pushed the nesposito/live-schedule-control-block branch from 4731727 to 8051b23 Compare August 28, 2026 16:01

@morgmart morgmart left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated code review

REQUEST_CHANGES: the client-side remove transaction can resume a schedule after another actor has paused it, and its rollback can hide the original deletion failure. Supplied GitHub checks completed successfully. Product and authorization questions require Morgan's decision.

Deterministic publication result: 2 blocking and 1 non-blocking finding(s) publishable; 0 duplicate(s) suppressed.

await client.goose.GooseUnstableSchedulesDelete({ scheduleId });
} catch (error) {
if (pausedForRemoval) {
await client.goose.GooseUnstableSchedulesUnpause({ scheduleId });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 P1 · Removal can resume paused work (blocking)

Removal reads the schedule, pauses it, attempts deletion, and unconditionally unpauses it if deletion fails. Another client can intentionally pause the schedule after this transaction begins; rollback then overwrites that newer state and resumes future runs.

User effect: After a failed removal, a schedule someone intentionally paused can start running again without their consent.

Recommended fix: Move remove-if-idle semantics to the authoritative scheduler as an atomic operation. If unavailable, re-read and verify ownership of the temporary transition before compensating, and share the lifecycle operation between UI and berdctl.

Test: Exercise a delete failure while a concurrent actor pauses the schedule after the temporary pause; verify rollback does not unpause the newer state.

await client.goose.GooseUnstableSchedulesDelete({ scheduleId });
} catch (error) {
if (pausedForRemoval) {
await client.goose.GooseUnstableSchedulesUnpause({ scheduleId });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 P1 · Rollback hides removal failure (blocking)

The delete catch block awaits unpause before rethrowing the deletion error. If unpause also fails, the rollback failure replaces the primary deletion failure, and the failed mutation does not refresh schedule state.

User effect: A failed removal can leave future runs paused while Berd reports the wrong failure and continues showing stale schedule status.

Recommended fix: Preserve the deletion error as primary, attach rollback failure as context, and refresh schedule state after a failure that may have changed pause state.

Test: Make delete and unpause fail together; assert the deletion error remains primary, rollback failure is retained, and the UI refreshes authoritative state.

: ""}
</div>
</div>
<div className="flex items-center gap-1.5">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 P2 · Schedule actions can overflow (non-blocking)

Each row may show three text buttons in a flex container that cannot wrap. Longer localized labels can overflow at narrow widths or increased text size.

User effect: At compact window widths or larger text settings, schedule controls can be clipped or extend outside their panel.

Recommended fix: Allow the action group to wrap or stack at compact widths using existing responsive layout and button primitives.

Test: Add a narrow responsive component case with Spanish copy and verify every action remains within the panel and reachable.

@morgmart

morgmart commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

How is the public vs. internal Automations experience intended to work here? My understanding is that Goose schedules can be available in the open-source build, while G2 automations are internal-only. If this is meant to support the public Automations surface, should the G2-specific content and flows be hidden there so Goose schedules work independently? If this PR is only adding schedules to the existing internal surface, the current separation seems fine for this scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants